"""
主进程，基于streamlit，展示各项工呢
"""
import json
import pickle
import copy
import time
import logging
import logging.handlers
import base64
import pickle
import io

import pandas as pd
import yaml
import os
from glob import iglob
from yaml.loader import SafeLoader
import requests

import streamlit as st
from streamlit_echarts import st_echarts
from function import *

st.set_page_config(layout="wide")


def pickle_decode(string):
    return pickle.loads(base64.b64decode(string))
    # pickle.loads(bytes(string))


def get_url(route):
    return "http://" + cfg['front_host'] + ':' + str(cfg['front_port']) + '/' + cfg[route]


@st.cache_resource
def init_logger(name,
                file_name='./log/viewer.log',
                console_level="INFO",
                format="%(asctime)s-%(name)s-%(levelname)s-%(filename)s-%(lineno)d-%(message)s",
                file_level="INFO"):
    """
    创建logger实例，添加文件日志和控制台日志输出。
    - name: 日志的名称。
    - file_name: 文件日志的文件名，如果为None，则不输出到文件。
    - console_level: 控制台输出日志的级别，默认为DEBUG。
    - format: 日志格式，默认为时间-名称-级别-文件名-行号-消息。
    - file_level: 文件日志的级别，默认为INFO。
    """
    logger = logging.getLogger(name)
    logger.setLevel(logging.DEBUG)  # 设置最低日志级别为DEBUG
    fm = logging.Formatter(format)

    fh = logging.handlers.TimedRotatingFileHandler(file_name, when='midnight', backupCount=0)
    fh.setFormatter(fm)
    fh.setLevel(file_level)
    logger.addHandler(fh)

    # 控制台日志处理器
    ch = logging.StreamHandler()
    ch.setFormatter(fm)
    ch.setLevel(console_level)
    logger.addHandler(ch)

    return logger


def main():
    """
    对于回测结果的查看器
    """

    st.sidebar.title('回测策略查看器')
    stg_all = [stg for stg in cfg['front_all_strategy']]
    # options = ["index", "权益走势图", "K线图", "QuantStats 回测结果", "实盘回测对比"]
    options = ["日终对比", "历史对比", "历史行情查看器", "回测结果查看器"]
    selected_tab = st.sidebar.radio("请选择:", options, index=None)

    selected_stg = st.sidebar.selectbox("选择策略:", stg_all)
    st.write("You selected:", selected_tab)
    if selected_tab == '日终对比':
        url = get_url('front_day_compare')
        in_day_compare = get(url, {"stg": selected_stg})  # 获取当日account数据
        res = eval(in_day_compare)
        st.write(f"日收益是：{(res['day_return'])}%")

        st.write("账户情况是：")
        st.write(pickle_decode(res['account']))

        st.write("持仓是：")
        st.write(pickle_decode(res['pos']))

        st.write('在回测中，但不存在实盘中的交易:')
        st.dataframe(pickle_decode(res["out_put"]))

        st.write("合并后的df：")
        st.write(pickle_decode(res['cmp'])[res['od']])

        tp = pickle_decode(res['merged_df'])
        st.write('原始实盘交易记录：')
        st.write(tp)

        st.write("回测交易记录：")
        st.write(pickle_decode(res['trade_record']))

        st.write("实盘交易记录")
        st.write(pickle_decode(res['real_trade']))


    elif selected_tab == "历史行情查看器":
        # 获取可用的codelist
        url = get_url('code_list')
        code_list = get(url, {"stg": selected_stg})
        code_list = json.loads(code_list)
        instrument = st.selectbox('选择查看的标的:', options=code_list, index=None)

        url = get_url('trade_record')
        trade_record = pd.read_csv(url + f"?stg={selected_stg}")

        url = get_url('factor_feed')
        factor_feed = pickle_decode(get(url, {"stg": selected_stg}))

        url = get_url('kline_option')
        option = json.loads(get(url, {"stg": selected_stg, "symbol": instrument}))

        st_echarts(options=option, height="700px", width="100%")
        a, b = st.columns(2)
        if not trade_record.empty:
            with a:
                st.dataframe(trade_record)
        if factor_feed and instrument in factor_feed.get_all():
            with b:
                tp = factor_feed.get_all()[instrument].copy(deep=True)
                if 'time_delta_for_next_bar' in tp:
                    tp['time_delta_for_next_bar'] = str(factor_feed.get_all()[instrument]['time_delta_for_next_bar'])
                st.dataframe(tp)

    elif selected_tab == "历史对比":
        days = st.selectbox("历史查看期：(-1表示全部)", (20, -1))
        url = get_url('front_his_compare')
        res = get(url, {"stg": selected_stg, 'days': days})  # 获取后端对于绘图的结果，直接用于展示。
        res = json.loads(res)

        option = res['echart_option']
        st_echarts(options=option, height="700px", width="100%")

        st.write("合并后的df：")
        st.write(pickle_decode(res['cmp'])[res['od']])

        st.write('在回测中，但不存在实盘中的交易:')
        st.dataframe(pickle_decode(res["out_put"]))

        tp = pickle_decode(res['merged_df'])
        st.write('原始实盘交易记录：')
        st.write(tp)

        st.write("回测交易记录：")
        st.write(pickle_decode(res['trade_record']))

        st.write("实盘交易记录")
        st.write(pickle_decode(res['real_trade']))

        # st_echarts(option)

    elif selected_tab == "回测结果查看器":

        # 净值图
        r = st.selectbox('', ('Log', 'Plain'))
        resmple = st.selectbox('', ('ORIGIN', 'DAY'), index=1)
        url = get_url('backtest_echart_option')
        option = get(url, {'frequency': resmple, 'log_or_plain': r, 'stg': selected_stg})
        option = json.loads(option)
        st_echarts(options=option, height="700px", width="100%")

        # 回测结果
        url = get_url('backtest_result')
        ret = get(url, {"stg": selected_stg})
        ret = eval(ret)
        for item in ret:
            st.text(item)

        # quantstats 文件
        url = get_url('qs_file')
        ret = get(url, {"stg": selected_stg})
        st.markdown(ret, unsafe_allow_html=True)


def get_config():
    path = os.path.dirname(__file__) + '/config.yaml'
    if os.path.exists(path):
        with open(path) as file:
            config_ = yaml.load(file, Loader=SafeLoader)
        return config_
    else:
        print(f'{path}, 不存在配置文件！')
        exit(-1)


def get(url, params=None):
    response = requests.get(url, params=params)
    if response.status_code == 200:
        log.info(f'{url}请求成功!')
        # 打印响应内容
        return response.text
    else:
        log.error(f'请求失败，状态码:{response.status_code}, {url}, {params}')


if __name__ == '__main__':
    cfg = get_config()
    log = init_logger('viewer', file_level=cfg['front_logger_level'])
    main()
